package com.jason.rtsp.handler;

import com.jason.rtsp.controller.VideoController;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @创建人 zhouhx
 * @创建时间 2023/8/31 15:41
 * @描述
 */
@Slf4j
@Component
//共享Handler
@ChannelHandler.Sharable
public class RtspHandler extends SimpleChannelInboundHandler<Object> {

    private WebSocketServerHandshaker wsHandshaker;

    @Autowired
    private VideoController videoController;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        //HTTP请求
        if(msg instanceof FullHttpRequest){
            FullHttpRequest request = (FullHttpRequest) msg;
            QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
            if(!"/live".equals(queryStringDecoder.path())){
                log.info("请检查uri:{}",queryStringDecoder.path());
                errorResponse(ctx,HttpResponseStatus.BAD_REQUEST);
            }
            if(!request.decoderResult().isSuccess() || (!"websocket".equals(request.headers().get("Upgrade")))){
                log.info("HTTP请求...");
                sendFlvRequestHeaders(ctx);
            }else{
                WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory
                        (getWebSocketLocation(request),null,true,5*1024*1024);
                wsHandshaker = factory.newHandshaker(request);
                if(wsHandshaker==null){
                    WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
                }else{
                    HttpResponse rsp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
                    rsp.headers().set(HttpHeaderNames.SERVER, "Jason");
                    DefaultChannelPromise channelPromise = new DefaultChannelPromise(ctx.channel());

                    wsHandshaker.handshake(ctx.channel(), request, rsp.headers(), channelPromise);
                    videoController.setClients(ctx);
                }
            }
            //websocket请求
        }else if(msg instanceof WebSocketFrame){
            log.info("websocket 请求...");
            handlerWebSocketRequest(ctx, (WebSocketFrame) msg);
        }
    }

    private void errorResponse(ChannelHandlerContext ctx, HttpResponseStatus status) {
        log.info("请求地址有误");
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status,
                Unpooled.copiedBuffer("请求地址有误: " + status + "\r\n", CharsetUtil.UTF_8));
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    private String getWebSocketLocation(FullHttpRequest request){
        return  "ws://"+request.headers().get(HttpHeaderNames.HOST)+request.getUri();
    }

    private void sendFlvRequestHeaders(ChannelHandlerContext ctx){
        DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        response.headers().set(HttpHeaderNames.CONNECTION,HttpHeaderValues.CLOSE)
                .set(HttpHeaderNames.CONTENT_TYPE,"video/x-flv")
                .set(HttpHeaderNames.ACCEPT_RANGES,"bytes")
                .set(HttpHeaderNames.PRAGMA,"no-cache")
                .set(HttpHeaderNames.CACHE_CONTROL,"no-cache")
                .set(HttpHeaderNames.TRANSFER_ENCODING,HttpHeaderValues.CHUNKED)
                .set(HttpHeaderNames.SERVER,"jason");
        ctx.writeAndFlush(response);
    }

    private void handlerWebSocketRequest(ChannelHandlerContext ctx,WebSocketFrame frame){
        //close
        if(frame instanceof CloseWebSocketFrame){
            wsHandshaker.close(ctx.channel(),((CloseWebSocketFrame) frame).retain());
            return;
        }
        //PING/PONG
        if(frame instanceof PingWebSocketFrame){
            ctx.write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        //Text
        if(frame instanceof TextWebSocketFrame){
            frame.retain(1);
            ctx.channel().writeAndFlush(new TextWebSocketFrame(((TextWebSocketFrame) frame).text()));
            return;
        }
        if(frame instanceof BinaryWebSocketFrame){
            return;
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        log.info("连接异常..." + cause.getMessage());
        //关闭连接
        ctx.close();
     }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("web客户端连接..."+ctx.channel().id().asLongText());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("handlerRemoved ..." + ctx.channel().id().asLongText());
    }
}
